<?php

namespace Aoe\Util;

use DateTime;

class IdentityCard
{
    private static array $areas = [
        11 => "北京", 12 => "天津", 13 => "河北", 14 => "山西", 15 => "内蒙古",
        21 => "辽宁", 22 => "吉林", 23 => "黑龙江",
        31 => "上海", 32 => "江苏", 33 => "浙江", 34 => "安徽", 35 => "福建", 36 => "江西", 37 => "山东",
        41 => "河南", 42 => "湖北", 43 => "湖南", 44 => "广东", 45 => "广西", 46 => "海南",
        50 => "重庆", 51 => "四川", 52 => "贵州", 53 => "云南", 54 => "西藏",
        61 => "陕西", 62 => "甘肃", 63 => "青海", 64 => "宁夏", 65 => "新疆",
        71 => "台湾", 81 => "香港", 82 => "澳门", 91 => "国外"
    ];

    public static function isValid(string $id): bool
    {
        //老身份证长度15位，新身份证长度18位
        $length = strlen($id);
        if ($length == 15) return static::_valid_15($id);
        if ($length == 18) return static::_valid_18($id);

        return false;
    }

    private static function _valid_15(string $id): bool
    {
        return preg_match('/^d{15}$/', $id)
            && static::_has_area(substr($id, 0, 2))
            && static::_valid_date(substr($id, 6, 6));
    }

    private static function _valid_18(string $id): bool
    {
        return preg_match('/^\d{17}[0-9xX]$/', $id)
            && static::_has_area(substr($id, 0, 2))
            && static::_valid_date(substr($id, 6, 6))
            && static::_verify_code($id);
    }

    private static function _has_area(string $area): bool
    {
        return isset(static::$areas[$area]);
    }

    private static function _valid_date(string $date): bool
    {
        //15位身份证号没有年份，这里拼上年份
        if (strlen($date) == 6) $date = '19' . $date;

        $day = DateTime::createFromFormat('Ymd', $date);
        return $day && $day->getTimestamp() < time();
    }


    /**
     * 验证18位身份证最后一位
     * @param string $id 待校验的身份证号
     * @return bool
     */
    private static function _verify_code(string $id): bool
    {
        if (strlen($id) !== 18) return false;

        $factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
        $tokens = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

        $checkSum = 0;
        for ($i = 0; $i < 17; $i++) $checkSum += intval($id[$i]) * $factor[$i];

        return strtoupper($id[17]) === $tokens[$checkSum % 11];
    }
}